#include "evaluator.h"

const int Evalutor::position_value[15][15] = {
    {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,},
    {0,1,1,1,1,1,1,1,1,1,1,1,1,1,0,},
    {0,1,2,2,2,2,2,2,2,2,2,2,2,1,0,},
    {0,1,2,3,3,3,3,3,3,3,3,3,2,1,0,},
    {0,1,2,3,4,4,4,4,4,4,4,3,2,1,0,},
    {0,1,2,3,4,5,5,5,5,5,4,3,2,1,0,},
    {0,1,2,3,4,5,6,6,6,5,4,3,2,1,0,},
    {0,1,2,3,4,5,6,7,6,5,4,3,2,1,0,},
    {0,1,2,3,4,5,6,6,6,5,4,3,2,1,0,},
    {0,1,2,3,4,5,5,5,5,5,4,3,2,1,0,},
    {0,1,2,3,4,4,4,4,4,4,4,3,2,1,0,},
    {0,1,2,3,3,3,3,3,3,3,3,3,2,1,0,},
    {0,1,2,2,2,2,2,2,2,2,2,2,2,1,0,},
    {0,1,1,1,1,1,1,1,1,1,1,1,1,1,0,},
    {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,},
};

Evalutor::Evalutor()
{

}

Evalutor::~Evalutor()
{

}

long Evalutor::Evaluate( Byte matrix[15][15], ChessType side )
{
    // analysis horizon direction
    for (int i = 0; i < 15; i++) {
        Byte line[15] = {0};
        for (int j = 0; j < 15; j++) {
            line[j] = matrix[i][j];
        }
        AnalysisLine(line, 15, 0, i);
    }

    // analysis vertical direction
    for (int i = 0; i < 15; i++) {
        Byte line[15] = {0};
        for (int j = 0; j < 15; j++) {
            line[j] = matrix[j][i];
        }
        AnalysisLine(line, 15, 1, i);
    }

    // analysis clockwise 45 degree
    for (int i = 0; i < 29; i++) {
        Byte line[15] = {0};
        int len, x, y;
        if (i < 15) {
            len = i + 1;
            x = 0;
            y = i;
        } else {
            len = 29 - i;
            x = i - 14;
            y = 14;
        }
        for (int j = 0; j < len; j++) {
            line[j] = matrix[x + j][y - j];
        }
        AnalysisLine(line, len, 2, i);
    }

    // analysis anti-clockwise 45 degree
    for (int i = 0; i < 29; i++) {
        Byte line[15] = {0};
        int len, x, y;
        if (i < 15) {
            len = i + 1;
            x = 0;
            y = 14 - i;
        } else {
            len = 29 - i;
            x = i - 14;
            y = 0;
        }
        for (int j = 0; j < len; j++) {
            line[j] = matrix[x + j][y + j];
        }
        AnalysisLine(line, len, 3, i);
    }

    // add up all chess type
    long type_count[2][7] = {0};
    for (int i = 0; i < 2; i++) {
        for (int j = 0; j < 15; j++)
            for (int k = 0; k < 2; k++)
                for (int m = 0; m < 7; m++) {
                    type_count[k][m] += straight_type_count[i][j][k][m];
                }
    }

    for (int i = 0; i < 2; i++) {
        for (int j = 0; j < 29; j++)
            for (int k = 0; k < 2; k++)
                for (int m = 0; m < 7; m++) {
                    type_count[k][m] += oblique_type_count[i][j][k][m];
                }
    }

    // five
    if (kBlack == side) {
        if (type_count[0][kFive]) return 9999;
        if (type_count[1][kFive]) return -9999;
    } else {
        if (type_count[0][kFive]) return -9999;
        if (type_count[1][kFive]) return 9999;
    }

    // two rushing four equals a alive four
    if (type_count[0][kRushingFour] > 1)
        type_count[0][kAliveFour]++;
    if (type_count[1][kRushingFour] > 1)
        type_count[1][kAliveFour]++;

    int black_value = 0;
    int white_value = 0;
    if (kBlack == side) {
        if (type_count[0][kAliveFour]) return 9990;
        if (type_count[0][kRushingFour]) return 9980;
        if (type_count[1][kAliveFour]) return -9970;
        if (type_count[1][kRushingFour] && type_count[1][kAliveThree])
            return -9960;
        if (type_count[0][kAliveThree] && 
            0 == type_count[1][kRushingFour])
            return 9950 + type_count[0][kAliveThree];
        if (type_count[1][kAliveThree] > 1 &&
            0 == type_count[0][kRushingFour] &&
            0 == type_count[0][kAliveThree] &&
            0 == type_count[0][kSleepingThree]) 
            return -9940 - type_count[1][kAliveThree];

        if (type_count[0][kAliveThree] > 1) black_value += 2000;
        else if(type_count[0][kAliveThree]) black_value += 200;

        if (type_count[1][kAliveThree] > 1) white_value += 500;
        else if(type_count[1][kAliveThree]) white_value += 100;

        white_value += type_count[1][kSleepingThree] * 10;
        black_value += type_count[0][kSleepingThree] * 10;
        white_value += type_count[1][kAliveTwo] * 4;
        black_value += type_count[0][kAliveTwo] * 4;
        white_value += type_count[1][kSleepingTwo] * 1;
        black_value += type_count[0][kSleepingTwo] * 1;

    } else {
        if (type_count[1][kAliveFour]) return 9990;
        if (type_count[1][kRushingFour]) return 9980;
        if (type_count[0][kAliveFour]) return -9970;
        if (type_count[0][kRushingFour] && type_count[0][kAliveThree])
            return -9960;
        if (type_count[1][kAliveThree] && 
            0 == type_count[0][kRushingFour])
            return 9950 + type_count[1][kAliveThree];
        if (type_count[0][kAliveThree] > 1 &&
            0 == type_count[1][kRushingFour] &&
            0 == type_count[1][kAliveThree] &&
            0 == type_count[1][kSleepingThree]) 
            return -9940 - type_count[0][kAliveThree];

        if (type_count[1][kAliveThree] > 1) white_value += 2000;
        else if(type_count[1][kAliveThree]) white_value += 200;

        if (type_count[0][kAliveThree] > 1) black_value += 500;
        else if(type_count[0][kAliveThree]) black_value += 100;

        white_value += type_count[1][kSleepingThree] * 10;
        black_value += type_count[0][kSleepingThree] * 10;
        white_value += type_count[1][kAliveTwo] * 4;
        black_value += type_count[0][kAliveTwo] * 4;
        white_value += type_count[1][kSleepingTwo] * 1;
        black_value += type_count[0][kSleepingTwo] * 1;

    }

    for (int i = 0; i < 15; i++) {
        for (int j = 0; j < 15; j++) {
            if (matrix[i][j] != kNoChess) {
                if (matrix[i][j] == kBlack) black_value += position_value[i][j];
                else white_value += position_value[i][j];
            }
        }
    }

    if (kBlack == side) return black_value - white_value;
    else return white_value - black_value;
}

void Evalutor::AnalysisLine( Byte* line, int length, int dir, int lnum )
{
    // clear type count of this line
    if (0 == dir || 1 == dir) {
        for (int i = 0; i < 2; i++) {
            for (int j = 0; j < 7; j++) {
                straight_type_count[dir][lnum][i][j] = 0;
            }
        }
    } else {
        for (int i = 0; i < 2; i++) {
            for (int j = 0; j < 7; j++) {
                oblique_type_count[dir - 2][lnum][i][j] = 0;
            }
        }
    }

    // if length of a line is less than 5, it's meaningless
    if (length < 5) {
        return;
    }

    // chess status
    int* chess_status = new int[length];

    // initialize chess status to not analyzed
    for (int i = 0; i < length; i++) {
        chess_status[i] = kNotAnalyzed;
    }
    
    // analysis
    for (int i = 0; i < length; i++) {
        // if this chess is not analyzed ,
        if (chess_status[i] == kNotAnalyzed) {
            // if no chess, continue
            if (line[i] == kNoChess) {
                continue;
            } else {
                // chess type of current chess being analyzed
                Byte chess_type = line[i];
                Byte opposite_type = chess_type == kBlack ? kWhite : kBlack;
                int ledge, redge;
                int lrange, rrange;
                ledge = redge = i;

                // calculate left edge of the chess type 
                while(ledge > 0) {
                    if (line[ledge - 1] != chess_type) break;
                    ledge--;
                }

                // calculate right edge of the chess type
                while(redge < length - 1) {
                    if (line[redge + 1] != chess_type) break;
                    redge++;
                }

                lrange = ledge;
                rrange = redge;
                // calculate the left range of position that can down a chess
                while(lrange > 0) {
                    if (line[lrange - 1] != kNoChess 
                        && line[lrange - 1] != chess_type) break;
                    lrange--;
                }
                // calculate the right range of position that can down a chess
                while(rrange < length - 1) {
                    if (line[rrange + 1] != kNoChess
                        && line[rrange + 1] != chess_type) break;
                    rrange++;
                }

                // if the range is less than 5, it's meaningless
                if (rrange - lrange < 4) {
                    for (int k = lrange; k <= rrange; k++) {
                        chess_status[k] = kAnalyzed;
                    }
                    continue;
                }

                // set continuous chess status to analyzed to prevent repeated
                // analysis
                for (int k = ledge; k <= redge; k++) {
                    chess_status[k] = kAnalyzed;
                }

                // if five in a row
                if (redge - ledge > 3) {
                    chess_status[i] = kFive;
                    continue;
                }

                // four continuous chess
                if (3 == redge - ledge) {
                    if(ledge > 0 && kNoChess == line[ledge - 1] &&
                        redge < length - 1 && kNoChess == line[redge + 1]) {
                        chess_status[i] = kAliveFour;
                    } else {
                        chess_status[i] = kRushingFour;
                    }
                    continue;
                }

                // three continuous chess
                if (2 == redge - ledge) {
                    // left has no air
                    if (0 == ledge || kNoChess != line[ledge - 1]) {
                        if (line[redge + 2] == chess_type) {
                            chess_status[ledge] = kRushingFour;
                            chess_status[redge + 2] = kAnalyzed;
                            int i = 3;
                            while(redge + i < length && line[redge + i] == chess_type) {
                                chess_status[redge + i] = kAnalyzed;
                                i++;
                            }

                        } else {
                            chess_status[ledge] = kSleepingThree;
                        }
                        continue;
                    }

                    // right has no air
                    if (length - 1 == redge || kNoChess != line[redge + 1]) {
                        if (line[ledge - 2] == chess_type) {
                            chess_status[redge] = kRushingFour;
                            chess_status[ledge - 2] = kAnalyzed;
                            int i = 3;
                            while(ledge - i >= 0 && line[ledge - i] == chess_type) {
                                chess_status[ledge - i] = kAnalyzed;
                                i++;
                            }
                        } else {
                            chess_status[redge] = kSleepingThree;
                        }
                        continue;
                    }

                    // the continuous chess has both left and right air
                    if (4 == rrange - lrange) {
                        chess_status[i] = kSleepingThree;
                        continue;
                    } else {
                        int c = 0;
                        if (ledge - 2 >= 0 && chess_type == line[ledge - 2]) {
                            chess_status[redge] = kRushingFour;
                            chess_status[ledge - 2] = kAnalyzed;
                            int i = 3;
                            while(ledge - i >= 0 && line[ledge - i] == chess_type) {
                                chess_status[ledge - i] = kAnalyzed;
                                i++;
                            }
                            c++;
                        }

                        if (redge + 2 <= length - 1 && chess_type == line[redge + 2]) {
                            chess_status[ledge] = kRushingFour;
                            chess_status[redge + 2] = kAnalyzed;
                            int i = 3;
                            while(redge + i < length && line[redge + i] == chess_type) {
                                chess_status[redge + i] = kAnalyzed;
                                i++;
                            }
                            c++;
                        }
                        if (0 == c) {
                            chess_status[i] = kAliveThree;
                        }
                        continue;
                    }
                }

                // two continuous chess
                if (1 == redge - ledge) {
                    // both left and right air
                    if (ledge > 0 && kNoChess == line[ledge - 1] 
                    && redge < length - 1 && kNoChess == line[redge + 1]) {
                        // analysis right
                        // _OO_X
                        if (redge + 2 > length - 1 
                            || line[redge + 2] == opposite_type) {
                            chess_status[ledge] = kAliveTwo;
                        // _OO_*X
                        } else if (redge + 3 > length - 1
                            || line[redge + 3] == opposite_type) {
                            // _OO_OX
                            if (line[redge + 2] == chess_type){
                                chess_status[ledge] = kSleepingThree;
                                chess_status[redge + 2] = kAnalyzed;
                            // _OO__X
                            } else {
                                chess_status[ledge] = kAliveTwo;
                            }
                        // _OO_**
                        } else {
                            bool r2 = chess_type == line[redge + 2];
                            bool r3 = chess_type == line[redge + 3];
                            switch(r2) {
                                case true:
                                    switch (r3) {
                                        case true:
                                            // _OO_OO, rushing four
                                            chess_status[ledge] = kRushingFour;
                                            chess_status[redge + 2] = kAnalyzed;
                                            chess_status[redge + 3] = kAnalyzed;
                                            break;
                                        case false:
                                            // _OO_O_, sleeping three;
                                            chess_status[ledge] = kAliveThree;
                                            chess_status[redge + 2] = kAnalyzed;
                                            break;
                                    }
                                    break;
                                case false:
                                    switch (r3) {
                                        case true:
                                            // _OO__O, sleeping three;
                                            chess_status[ledge] = kSleepingThree;
                                            chess_status[ redge + 3] = kAnalyzed;
                                            break;
                                        case false:
                                            // _OO___, sleeping two;
                                            chess_status[ledge] = kAliveTwo;
                                            break;

                                    }
                                    break;
                            }
                        }
                        // analysis left
                        // X_OO_
                        if (ledge - 2 < 0 || 
                            line[ledge - 2] == opposite_type) {
                            if (chess_status[ledge] != kAliveTwo) {
                                chess_status[redge] = kAliveTwo;
                            }
                        // X*_OO_
                        } else if (ledge - 3 < 0
                            || line[ledge - 3] == opposite_type) {
                            // XO_OO_
                            if (line[ledge - 2] == chess_type) {
                                chess_status[redge] = kSleepingThree;
                                chess_status[ledge - 2] = kAnalyzed;
                            // X__OO_
                            } else {
                                if (chess_status[ledge] != kAliveTwo) {
                                    chess_status[redge] = kAliveTwo;
                                }
                            }
                        // **_OO_
                        } else {
                            bool l2 = chess_type == line[ledge - 2];
                            bool l3 = chess_type == line[ledge - 3];
                            switch(l2) {
                                case true:
                                    switch (l3) {
                                        case true:
                                            // OO_OO_, rushing four
                                            chess_status[redge] = kRushingFour;
                                            chess_status[ledge - 2] = kAnalyzed;
                                            chess_status[ledge - 3] = kAnalyzed;
                                            break;
                                        case false:
                                            // _O_OO_, sleeping three;
                                            chess_status[redge] = kAliveThree;
                                            chess_status[ledge - 2] = kAnalyzed;
                                            break;
                                            }
                                            break;
                                case false:
                                    switch (l3) {
                                        case true:
                                            // O__OO_, sleeping three;
                                            chess_status[redge] = kSleepingThree;
                                            chess_status[ledge - 3] = kAnalyzed;
                                            break;
                                        case false:
                                            // ___OO_, sleeping two;
                                            if (chess_status[ledge] != kAliveTwo) {
                                                chess_status[redge] = kAliveTwo;
                                            }
                                            break;

                                    }
                                    break;
                            }
                        }
                    } else {
                        // right air
                        if (0 == ledge || kNoChess != line[ledge - 1]) {
                            bool r2 = chess_type == line[redge + 2];
                            bool r3 = chess_type == line[redge + 3];
                            switch(r2) {
                                case true:
                                    switch (r3) {
                                        case true:
                                            // XOO_OO, rushing four
                                            chess_status[ledge] = kRushingFour;
                                            chess_status[redge + 2] = kAnalyzed;
                                            chess_status[redge + 3] = kAnalyzed;
                                            break;
                                        case false:
                                            // XOO_O_, sleeping three;
                                            chess_status[ledge] = kSleepingThree;
                                            chess_status[redge + 2] = kAnalyzed;
                                            break;
                                    }
                                    break;
                                case false:
                                    switch (r3) {
                                        case true:
                                            // XOO__O, sleeping three;
                                            chess_status[ledge] = kSleepingThree;
                                            chess_status[ redge + 3] = kAnalyzed;
                                            break;
                                        case false:
                                            // XOO___, sleeping two;
                                            chess_status[ledge] = kSleepingTwo;
                                            break;

                                    }
                                    break;
                            }
                            continue;
                        } // right air
                        // left air
                        if (length - 1 == redge || kNoChess != line[redge - 1]) {
                            bool l2 = chess_type == line[ledge - 2];
                            bool l3 = chess_type == line[ledge - 3];
                            switch(l2) {
                                case true:
                                    switch (l3) {
                                        case true:
                                            // OO_OOX, rushing four
                                            chess_status[redge] = kRushingFour;
                                            chess_status[ledge - 2] = kAnalyzed;
                                            chess_status[ledge - 3] = kAnalyzed;
                                            break;
                                        case false:
                                            // _O_OOX, sleeping three;
                                            chess_status[redge] = kSleepingThree;
                                            chess_status[ledge - 2] = kAnalyzed;
                                            break;
                                    }
                                    break;
                                case false:
                                    switch (l3) {
                                        case true:
                                            // O__OOX, sleeping three;
                                            chess_status[redge] = kSleepingThree;
                                            chess_status[ledge - 3] = kAnalyzed;
                                            break;
                                        case false:
                                            // ___OOX, sleeping two;
                                            chess_status[redge] = kSleepingTwo;
                                            break;

                                    }
                                    break;
                            }
                            continue;
                        } // left air
                    }
                }

            } // if (line[i] == kNoChess) else
        } // if (chess_status[i] == kNotAnalyzed)
    } // for (int i = 0; i < length; i++)

    // straight direction    
    if (0 == dir || 1 == dir) {
        // add up each type count
        for (int i = 0; i < length; i++) {
            Byte chess_type = line[i];
            if (kNoChess != chess_type && kAnalyzed != chess_status[i]) {
                straight_type_count[dir][lnum][chess_type - 1][chess_status[i]]++;
            }
        }

    // oblique direction
    } else {
        // add up each type count
        for (int i = 0; i < length; i++) {
            Byte chess_type = line[i];
            if (kNoChess != chess_type && kAnalyzed != chess_status[i]) {
                oblique_type_count[dir - 2][lnum][chess_type - 1][chess_status[i]]++;
            }
        }

    }

    delete chess_status;
}